package com.sxj.shop.util;

import com.sxj.shop.exception.DataAccessException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * JDBC模板方法:对JDBC访问数据库的操作进行封装,简化JDBC的操作
 */
public class JDBCTemplate<T> {

    /**
     * 执行一个DML操作。即:insert、delete、update
     * 对于DML操作,使用ps.executeUpdate(sql)来执行sql
     * 对于整个方法的过程而言,有且仅有sql与参数不同,其他部分代码基本一致
     * 我们可以相同的地方封装起来,不同的地方作为方法的参数,由方法调用者在进行调用时具体传入
     * @param sql    需要执行的sql语句
     * @param params 执行sql语句所需要的参数
     */
    public void update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = JDBCUtil.getConnection();
            // 获取状态集
            ps = conn.prepareStatement(sql);
            // 设置参数
            for (int i = 0; i < params.length; i++) {
                Object param = params[i];
                // 由于参数的类型是方法调用者在进行调用时才能确定的,此处无法预先知道参数类型
                // 因此,我们可以通过判断类型的方式进行实现
//                if(param instanceof String){
//                    ps.setString(i+1,(String)param);
//                }
                // 但是,由于可能的类型比较多,我们可以直接通过setObject表示所有类型
                ps.setObject(i + 1, param);
            }
            // 执行sql
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            throw new DataAccessException("数据访问异常");
        } finally {
            // 由于在业务层需要使用的事务,而事务必须用到连接
            // 因此,此处的连接不能释放,如果不想释放,参数值给null即可
            JDBCUtil.close(null, ps, null);
        }
    }

    /**
     * 执行一个查询操作
     * 查询操作会返回一个结果集,结果集中的数据可能有一行,也可能有多行
     * 因此,通过一个集合接收结果集的数据
     * 对于查询操作而言,需要将结果集的数据映射成Java的对象
     * 但是,此时我们不知道最终需要映射的对象是谁,只有在方法调用者进行调用时才能最终确定
     * 我们此处可以知道的是将一个结果集映射成某个Java对象
     * 因此,可以通过接口来定义规则,此处通过接口进行使用
     * 在方法调用者具体实现时对接口的方法进行实现即可
     * @param sql    需要执行的sql语句
     * @param rm     行映射接口,将结果集转换成Java对象
     * @param params 执行sql语句所需要的参数
     * @return
     */
    public List<T> query(String sql, RowMapper<T> rm, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<>();

        try {
            conn = JDBCUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            // 执行查询操作,返回结果集
            rs = ps.executeQuery();
            // 执行行映射
            while (rs.next()) {
                T t = rm.mapRow(rs);
                // 每映射一条数据,将映射得到的Java对象加入到集合中
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            throw new DataAccessException("数据访问异常");
        } finally {
            JDBCUtil.close(null, ps, rs);
        }
    }

}
